local constrained_size = require 'constrained_size'
local constraints = require 'constraints'
local multi_child_view = require 'views.multi_child_view'
local rect = require 'rect'
local vec2 = require 'vec2'

---@enum layout_direction
local direction = {
    horizontal = 0,
    vertical = 1
}

---@class list: multi_child_view
---@field direction layout_direction
local list = multi_child_view:new_view_type()

list.layout_direction = direction

---@class list.renderer: multi_child_view.renderer
list.renderer = multi_child_view.renderer:new_renderer_type()

function list:__call(options)
    self:set_view_prototype(options)

    options.children = options.children or {}
    options.direction = options.direction or list.layout_direction.horizontal

    return options
end

---@param constraints constraints
---@param view list
function list.renderer:layout(constraints, view)
    return constrained_size.unconstrained()
end

---@param render_rect rect
---@param view list
function list.renderer:render(render_rect, view)
    local render_rect_size = render_rect:size()
    local child_count = #view.children
    local size_per_unconstrainted_child = self:__calculate_size_per_unconstrained_child(
        render_rect, view)

    local offset = vec2(0)
    local child_constraints = constraints.new(vec2(0), render_rect_size)

    for i = 1, child_count do
        local child_view = view.children[i]
        local child_renderer = self.child_renderers[i]
        local child_size = child_renderer:layout(child_constraints, child_view)
        local top_left = vec2(render_rect:left() + offset.x, render_rect:top() - offset.y)

        if view:__is_unconstrained_by_direction(child_size) then
            local child_rect = rect.from_top_left(top_left, size_per_unconstrainted_child)
            child_renderer:render(child_rect, child_view)

            if view.direction == direction.horizontal then
                offset.x = offset.x + size_per_unconstrainted_child.x
            else
                offset.y = offset.y + size_per_unconstrainted_child.y
            end
        else
            local child_rect = rect.from_top_left(
                top_left,
                child_size:to_vec2_or(render_rect_size)
            )
            child_renderer:render(child_rect, child_view)

            if view.direction == direction.horizontal then
                offset.x = offset.x + child_size.width
            else
                offset.y = offset.y + child_size.height
            end
        end
    end
end

---@private
---@param render_rect rect
---@param view list
function list.renderer:__calculate_size_per_unconstrained_child(render_rect, view)
    local render_rect_size = render_rect:size()
    local child_count = #view.children
    local available_size = render_rect_size:copy()
    local all_constrained_children_size = vec2(0)
    local constrainted_child_count = 0

    for i = 1, child_count do
        local child_view = view.children[i]
        local child_renderer = self.child_renderers[i]
        local child_constraints = constraints.new(vec2(0), available_size)
        local child_size = child_renderer:layout(child_constraints, child_view)

        if view:__is_unconstrained_by_direction(child_size) == false then
            if view.direction == direction.horizontal then
                local width = child_size.width
                available_size.x = available_size.x - width
                all_constrained_children_size.x = all_constrained_children_size.x + width
            else
                local height = child_size.height
                available_size.y = available_size.y - height
                all_constrained_children_size.y = all_constrained_children_size.y + height
            end

            constrainted_child_count = constrainted_child_count + 1
        end
    end

    local unconstrained_child_count = child_count - constrainted_child_count
    local unconstrained_children_size = render_rect_size - all_constrained_children_size
    local size_per_unconstrainted_child = render_rect_size:copy()

    if view.direction == direction.horizontal then
        size_per_unconstrainted_child.x =
            unconstrained_children_size.x / unconstrained_child_count
    else
        size_per_unconstrainted_child.y =
            unconstrained_children_size.y / unconstrained_child_count
    end

    return size_per_unconstrainted_child
end

---@package
---@param size constrained_size
function list:__is_unconstrained_by_direction(size)
    return
        (self.direction == direction.horizontal and size.width_unconstrained) or
        (self.direction == direction.vertical and size.height_unconstrained)
end

return list
